/*
 * Copyright 2007-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.paoding.rose.web.impl.thread;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.paoding.rose.RoseConstants;
import net.paoding.rose.web.annotation.AsSuperController;
import net.paoding.rose.web.annotation.Ignored;
import net.paoding.rose.web.annotation.REST;
import net.paoding.rose.web.annotation.ReqMapping;
import net.paoding.rose.web.annotation.ReqMethod;
import net.paoding.rose.web.impl.mapping.Mapping;
import net.paoding.rose.web.impl.mapping.MappingFactory;
import net.paoding.rose.web.impl.module.Module;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.ClassUtils;

/**
 * 
 * @author qieqie.wang@paoding.net
 * 
 */
public class ControllerEngine {

	private static Log logger = LogFactory.getLog(ControllerEngine.class);

	private Module module;
	private String controllerPath;
	private Object controller;
	private boolean proxiedController;
	private Class<?> controllerClass;
	private String viewPrefix;
	private ArrayList<Mapping<ActionEngine>> actionMappings = new ArrayList<Mapping<ActionEngine>>();
	private String controllerName;
	private Map<String, String[]> restSetting = new HashMap<String, String[]>();
	

	public ControllerEngine(Module module, String controllerPath,
			Object controller, Class<?> controllerClass) {
		this.module = module;
		this.controllerPath = controllerPath;
		this.controller = controller;
		this.controllerClass = controllerClass;
		this.proxiedController = Proxy.isProxyClass(this.controller.getClass());
		if (logger.isDebugEnabled()) {
			logger.debug("it's a proxied controller: "
					+ controllerClass.getName());
		}
		controllerName = ClassUtils.getShortNameAsProperty(controllerClass);
		this.viewPrefix = controllerName.substring(0, controllerName.length()
				- RoseConstants.CONTROLLER_SUFFIX.length())
				+ "-";
		init();
	}

	public String getControllerName() {
		return controllerName;
	}

	public Module getModule() {
		return module;
	}

	public String getViewPrefix() {
		return viewPrefix;
	}

	public String getControllerPath() {
		return controllerPath;
	}

	public Object getController() {
		return controller;
	}

	public Class<?> getControllerClass() {
		return controllerClass;
	}

	public boolean isProxiedController() {
		return proxiedController;
	}

	protected void init() {
		Class<?> clz = controllerClass;// 从clz得到的method不是aop后controller的clz阿!!!
		//
		List<Method> pastMethods = new LinkedList<Method>();
		while (true) {
			Method[] declaredMethods = clz.getDeclaredMethods();
			for (Method method : declaredMethods) {
				if (!Modifier.isPublic(method.getModifiers())) {
					continue;
				}
				// 去除已经标志不作为接口的方法
				if (method.getAnnotation(Ignored.class) != null) {
					continue;
				}
				// 刚才在继承类(子类)已经声明的方法，不必重复处理了
				boolean broken = false;
				for (Method past : pastMethods) {
					if (past.getName().equals(method.getName())) {
						if (Arrays.equals(past.getParameterTypes(), method
								.getParameterTypes())) {
							broken = true;
							break;
						}
					}
				}
				if (broken) {
					continue;
				}

				String methodPath = "/" + method.getName();
				ActionEngine actionEngine = new ActionEngine(this,
						method);
				// 解析@ReqMapping
				ReqMapping reqMappingAnnotation = method
						.getAnnotation(ReqMapping.class);
				ReqMethod[] methods;
				String[] mappingPaths;
				if (reqMappingAnnotation != null) {
					methods = reqMappingAnnotation.methods();
					mappingPaths = reqMappingAnnotation.path();
					// 如果mappingPaths.length==0，表示没有任何path可以映射到这个action了
					for (int i = 0; i < mappingPaths.length; i++) {
						if (ReqMapping.DEFAULT_PATH.equals(mappingPaths[i])) {
							mappingPaths[i] = methodPath;
						} else if (mappingPaths[i].length() > 0
								&& mappingPaths[i].charAt(0) != '/') {
							mappingPaths[i] = '/' + mappingPaths[i];
						} else if (mappingPaths[i].equals("/")) {
							mappingPaths[i] = "";
						}
					}
				} else {
					methods = new ReqMethod[] { ReqMethod.ALL };
					mappingPaths = new String[] { methodPath };
				}
				if (mappingPaths.length > 0 && methods.length > 0) {
					for (int i = 0; i < mappingPaths.length; i++) {
						this.actionMappings.add(MappingFactory.createMapping(
								mappingPaths[i], MappingFactory.MODE_EQUALS, methods, actionEngine));
					}
				}
			}
			for (int i = 0; i < declaredMethods.length; i++) {
				pastMethods.add(declaredMethods[i]);
			}
			clz = clz.getSuperclass();
			if (clz == null
					|| clz.getAnnotation(AsSuperController.class) == null) {
				break;
			}
		}
		boolean showIdExists = false;
		for (int i = 1; i < actionMappings.size(); i++) {
			Mapping<ActionEngine> mapping = actionMappings.get(i);
			if (logger.isDebugEnabled()) {
				logger.debug("binding: " + module.getPath() + mapping);
			}
			if (!showIdExists && mapping.match("/198107", "GET") != null) {
				showIdExists = true;
			}
		}
		// /user/123456自动映射到UserController的show()方法
		if (!showIdExists) {
			for (int i = 1; i < actionMappings.size(); i++) {
				Mapping<ActionEngine> pathMapping = actionMappings.get(i);
				Method method = pathMapping.getMappingTarget().getMethod();
				if ("show".equals(method.getName()) && method.getAnnotation(ReqMapping.class) == null) {
					this.actionMappings.add(MappingFactory.createMapping(
							"/show/{id:[0-9]+}", MappingFactory.MODE_EQUALS, pathMapping.getMethods(), pathMapping.getMappingTarget()));
					this.actionMappings.add(MappingFactory.createMapping(
							"/{id:[0-9]+}", MappingFactory.MODE_EQUALS, pathMapping.getMethods(), pathMapping.getMappingTarget()));
					break;
				}
			}
		}
		Collections.sort(actionMappings);
		
		// URI没有提供action的处理(REST)
		REST rest = controllerClass.getAnnotation(REST.class);
		if (rest != null) {
			Method[] restMethods = rest.getClass().getMethods();
			for (Method restMethod : restMethods) {
				if (!ArrayUtils.contains(new String[]{
						"get", "post", "put", "options", "trace", "delete", "head"},
						restMethod.getName())) {
					continue;
				}
				if (restMethod.getParameterTypes().length != 0) {
					continue;
				}if (restMethod.getReturnType() != String[].class) {
					continue;
				}
				String[] candidates;
				try {
					candidates = (String[]) restMethod.invoke(rest);
					for (int i = 0; i < candidates.length; i++) {
						if (candidates[i].length() > 0
								&& candidates[i].charAt(0) != '/') {
							candidates[i] = '/' + candidates[i];
						}
					}
					restSetting.put(restMethod.getName().toUpperCase(), candidates);
					if (logger.isDebugEnabled()) {
						logger.debug(restMethod.getName().toUpperCase() + " " + module.getPath()
								+ controllerPath + "=" + Arrays.toString(candidates));
					}
				} catch (Exception e) {
					logger.error("error happened when invoking " + restMethod.getName(), e);
				}
			}
		} else {
			restSetting.put("GET", new String[] { "/index", "/get", "/render" });
			restSetting.put("POST", new String[] { "/post", "/update", });
		}
	}

	public Object[] searchAction(HttpServletRequest request,
			HttpServletResponse response, RequestPath requestPath)  {
		String actionPath;
		String requstMethod = request.getMethod();
		String controllerPathInfo = requestPath.getControllerPathInfo();
		
		// 计算actionPath，用于定位所要处理的actionEngine
		if (controllerPathInfo.startsWith(requestPath.getControllerPath())
				&& (controllerPathInfo.length() == requestPath.getControllerPath().length()
						|| controllerPathInfo.charAt(requestPath.getControllerPath().length()) == '/')) {
			// controllerPath是controllerMappingPath的一部分(大部分情况)！
			actionPath = controllerPathInfo.substring(requestPath.getControllerPath().length());
		} else {
			// 使用了defautlController的，此时整个controllerMappingPath都是actionPath
			actionPath = controllerPathInfo;
		}
		
		// 通过path找相应的ActionEngine
		MatchResult matchResult = null;
		Mapping<ActionEngine> mapping = null;
		for (Mapping<ActionEngine> m : actionMappings) {
			if (logger.isDebugEnabled()) {
				logger.debug("try matching(" + actionPath + "): " + m);
			}
			matchResult = m.match(actionPath, requstMethod);
			if (matchResult != null) {
				if (logger.isDebugEnabled()) {
					logger.debug("matched: " + m);
					logger.debug("matchResult.matchedString= " + matchResult.getMatchedString());
				}
				requestPath.setActionPath(matchResult.getMatchedString());
				mapping = m;
				break;
			}
		}
		// REST请求的?
		if (matchResult == null && actionPath.length() <= 1) {
			String[] candidates = restSetting.get(requstMethod);
			if (candidates == null) {
				candidates = new String[] { "/" + requstMethod.toLowerCase()};
				restSetting.put(requstMethod, candidates); // 自学习
			}
			if (logger.isDebugEnabled()) {
				logger.debug("actionPath is empty, try to use @REST candidates=" + Arrays.toString(candidates)
						+ " (" + requestPath.getUri() + ")");
			}
			for (Mapping<ActionEngine> actionMapping : actionMappings) {
				if (logger.isDebugEnabled()) {
					logger.debug("try matching(" + actionPath + "): " + actionMapping);
				}
				for (String candidate : candidates) {
					matchResult = actionMapping.match(candidate, requstMethod);
					if (matchResult != null) {
						break;
					}
				}
				if (matchResult != null) {
					if (logger.isDebugEnabled()) {
						logger.debug("matched(" + actionPath + "): " + actionMapping);
						logger.debug("matchResult.matchedString= " + matchResult.getMatchedString());
					}
					requestPath.setActionPath(matchResult.getMatchedString());
					mapping = actionMapping;
					break;
				}
			}
		}
		
		// 从这个Controller实在找不到对应的actionEngine，返回CONTINUE，获取由其它的Controller还能处理，或许吧!
		if (matchResult == null) {
			return null;
		}
		
		// 好！到此确认，这个请求应由Rose来处理，而且是这个Controller的这个action来处理
		return new Object[] {mapping, matchResult};
	}

	@Override
	public String toString() {
		return getControllerClass().getName();
	}

}
